]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
quickSettings: Add menu support to items
authorFlorian Müllner <fmuellner@gnome.org>
Fri, 29 Jul 2022 13:22:37 +0000 (15:22 +0200)
committerMarge Bot <marge-bot@gnome.org>
Wed, 3 Aug 2022 12:41:05 +0000 (12:41 +0000)
Now that the QuickSettingsMenu supports child menus, it is time
to add menu support to quick toggles.

Do that by introducing a QuickSettingsItem parent class with a
construct-only :has-menu property, as that will allow including
menus in items that aren't following the standard icon+label
pattern of QuickToggle (yes, we'll have some of those).

A common parent class also allows us to control the type of
menu that is created. That's important, as we need another
custom menu type to

- constrain the menu's y position to place it underneath
  the source actor

- use open/close animations taylored for this use case

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>

data/theme/gnome-shell-sass/widgets/_quick-settings.scss
js/ui/quickSettings.js

index 8dc5ff85087a051a8e583ccbee8e7c2f9c25525f..05d84951440620b8f13d894107a73c9bc15340af 100644 (file)
   .quick-toggle-label { font-weight: bold; }
   .quick-toggle-icon { icon-size: $base_icon_size; }
 }
+
+.quick-toggle-menu {
+  @include card();
+  padding: 1.5 * $base_padding;
+}
+
+.quick-toggle-menu-container {
+  padding: 2 * $base_padding $base_padding 0;
+ }
index 0e1b83cef1cf9affe210ef4648a0b2f5ece82f39..ae097ca6462255804d228fd91396c04cf21a2861 100644 (file)
@@ -4,8 +4,32 @@ const {Atk, Clutter, Gio, GLib, GObject, Graphene, Pango, St} = imports.gi;
 const Main = imports.ui.main;
 const PopupMenu = imports.ui.popupMenu;
 
-const {POPUP_ANIMATION_TIME} = imports.ui.boxpointer;
+const {PopupAnimation} = imports.ui.boxpointer;
+
 const DIM_BRIGHTNESS = -0.4;
+const POPUP_ANIMATION_TIME = 400;
+
+var QuickSettingsItem = GObject.registerClass({
+    Properties: {
+        'has-menu': GObject.ParamSpec.boolean(
+            'has-menu', 'has-menu', 'has-menu',
+            GObject.ParamFlags.READWRITE |
+            GObject.ParamFlags.CONSTRUCT_ONLY,
+            false),
+    },
+}, class QuickSettingsItem extends St.Button {
+    _init(params) {
+        super._init(params);
+
+        if (this.hasMenu) {
+            this.menu = new QuickToggleMenu(this);
+            this.menu.actor.hide();
+
+            this._menuManager = new PopupMenu.PopupMenuManager(this);
+            this._menuManager.addMenu(this.menu);
+        }
+    }
+});
 
 var QuickToggle = GObject.registerClass({
     Properties: {
@@ -17,7 +41,7 @@ var QuickToggle = GObject.registerClass({
             GObject.ParamFlags.READWRITE,
             ''),
     },
-}, class QuickToggle extends St.Button {
+}, class QuickToggle extends QuickSettingsItem {
     _init(params) {
         super._init({
             style_class: 'quick-toggle button',
@@ -70,6 +94,96 @@ var QuickToggle = GObject.registerClass({
     }
 });
 
+class QuickToggleMenu extends PopupMenu.PopupMenuBase {
+    constructor(sourceActor) {
+        super(sourceActor, 'quick-toggle-menu');
+
+        const constraints = new Clutter.BindConstraint({
+            coordinate: Clutter.BindCoordinate.Y,
+            source: sourceActor,
+        });
+        sourceActor.bind_property('height',
+            constraints, 'offset',
+            GObject.BindingFlags.DEFAULT);
+
+        this.actor = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            style_class: 'quick-toggle-menu-container',
+            reactive: true,
+            x_expand: true,
+            y_expand: false,
+            constraints,
+        });
+        this.actor._delegate = this;
+        this.actor.add_child(this.box);
+
+        global.focus_manager.add_group(this.actor);
+    }
+
+    open(animate) {
+        if (this.isOpen)
+            return;
+
+        this.actor.show();
+        this.isOpen = true;
+
+        this.actor.height = -1;
+        const [targetHeight] = this.actor.get_preferred_height(-1);
+
+        const duration = animate !== PopupAnimation.NONE
+            ? POPUP_ANIMATION_TIME / 2
+            : 0;
+
+        this.actor.height = 0;
+        this.box.opacity = 0;
+        this.actor.ease({
+            duration,
+            height: targetHeight,
+            onComplete: () => {
+                this.box.ease({
+                    duration,
+                    opacity: 255,
+                });
+                this.actor.height = -1;
+            },
+        });
+        this.emit('open-state-changed', true);
+    }
+
+    close(animate) {
+        if (!this.isOpen)
+            return;
+
+        const duration = animate !== PopupAnimation.NONE
+            ? POPUP_ANIMATION_TIME / 2
+            : 0;
+
+        this.box.ease({
+            duration,
+            opacity: 0,
+            onComplete: () => {
+                this.actor.ease({
+                    duration,
+                    height: 0,
+                    onComplete: () => {
+                        this.actor.hide();
+                        this.emit('menu-closed');
+                    },
+                });
+            },
+        });
+
+        this.isOpen = false;
+        this.emit('open-state-changed', false);
+    }
+
+    // expected on toplevel menus
+    _setOpenedSubMenu(submenu) {
+        this._openedSubMenu?.close(true);
+        this._openedSubMenu = submenu;
+    }
+}
+
 const QuickSettingsLayoutMeta = GObject.registerClass({
     Properties: {
         'column-span': GObject.ParamSpec.int(